#include "SceneWidget.h"
#include <SEEdit/Layer/EditLayer.h>
#include <SEUi/SEUi.h>
#include <SEngine/Render/Descriptors/SceneDescriptor.h>
#include <SEEdit/Descriptors/SelectActorDescriptor.h>
#include <SECore/Ecs/Scene.h>
#include <SEDebug/Log/Log.h>
#include <SEngine/Ecs/Components/NameComp.h>
#include <SEngine/Ecs/Components/UuidComp.h>
#include <SEngine/Ecs/Components/TransformComp.h>
#include <SEngine/Ecs/Components/LightComp.h>
#include <SECore/CoreApplication.h>
#include <SEngine/Project/Project.h>
#include <SEngine/Ecs/Components/ModelRenderComp.h>
#include <SEngine/Ecs/Components/LightComp.h>
#include <SEngine/Ecs/Components/CameraComp.h>

namespace SEngine
{

    SceneWidget::SceneWidget(EditLayer & layer)
        : Widget()
        , m_layer(layer)
    {
    }

    void SceneWidget::BeginUpdate()
    {

    }
    
    void SceneWidget::UpdateImpl(int delta)
    {
        if (ImGui::IsWindowHovered() && 
            ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
            !ImGui::IsAnyItemHovered()) {
            ResetSelectActor();
        }
        OnRightClickMenu();
        OnShowActor();
    }
    
    void SceneWidget::EndUpdate()
    {
    }

    void SceneWidget::OnShowActor()
    {
        
        auto project = m_layer.GetApp().GetDescriptor<Project>();
        if (!project) {
            return;
        }
        auto scene = project->get().GetScene();
        scene->Components<UuidComp>([&](Actor& actor, UuidComp & uuid) {
            if (!actor.GetParent()) {
                RecurseShowActor(actor);
            }
        });
    }

    void SceneWidget::OnItemRightClickMenu(Actor & actor)
    {
        auto project = m_layer.GetApp().GetDescriptor<Project>();
        if (!project) {
            return;
        }
        auto & scene = *project->get().GetScene();
        if (ImGui::BeginPopupContextItem()) {
            CreateMenu(actor.shared_from_this());
            if (ImGui::MenuItem("Edit")) {
                m_layer.GetApp().GetDescriptor<SelectActorDescriptor>()->get().selectActor = actor;
                m_layer.FocusWidget(WidgetsDescriptor::SE_AttributeView);
            }
            if (ImGui::MenuItem("Clone")) {
                m_layer.GetApp().SendTask([&](CoreApplication&) {
                    auto & newActor = m_layer.CloneActor(actor);
                    if (auto parent = actor.GetParent(); parent) {
                        parent.value()->AddChild(newActor);
                    }
                });
            }
            if (ImGui::MenuItem("Delete")) {
                m_layer.GetApp().SendTask([&](CoreApplication&) {
                    m_layer.DeleteActor(actor);
                });
            }
            ImGui::Separator();
            if (ImGui::MenuItem("Focus")) {
                OnFocusActor(actor);
            }
            ImGui::EndPopup();
        }    
    }

    void SceneWidget::RecurseShowActor(Actor &curActor)
    {
        auto selectActorDes = m_layer.GetApp().GetDescriptor<SelectActorDescriptor>();
        if (!selectActorDes) {
            return;
        }
        bool isSelected = selectActorDes->get().selectActor && (&selectActorDes->get().selectActor->get() == &curActor);
        auto & nameComp = curActor.GetComponent<NameComp>()->get();
        auto & uuidComp = curActor.GetComponent<UuidComp>()->get();
        ImGui::PushID(uuidComp.uuid.c_str());
        ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow;
        flags |= isSelected ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
        if (curActor.HasChildren()) {
            auto isExpand = ImGui::TreeNodeEx(nameComp.name.c_str(), flags); 
            if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
                selectActorDes->get().selectActor = curActor;
            }
            OnItemRightClickMenu(curActor);
            if (isExpand) {
                curActor.ForeachChildren([this](Actor & child) {
                    this->RecurseShowActor(child);
                });
                ImGui::TreePop();
            }
        } else {
            auto isExpand = ImGui::TreeNodeEx(nameComp.name.c_str(), flags | ImGuiTreeNodeFlags_Leaf);
            if (ImGui::IsItemClicked())
            if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
                selectActorDes->get().selectActor = curActor;
            }
            OnItemRightClickMenu(curActor);
            if (isExpand) {
                ImGui::TreePop();
            }
        }
        ImGui::PopID();
    }

    void SceneWidget::ResetSelectActor()
    {
        auto selectActorDes = m_layer.GetApp().GetDescriptor<SelectActorDescriptor>();
        if (!selectActorDes) {
            return;
        }
        selectActorDes->get().selectActor.reset();
    }

    void SceneWidget::CreateMenu(std::shared_ptr<Actor> actor)
    {
        auto CreateEmptyActor = [actor, this] (const std::string & name) {
            if (ImGui::MenuItem(name.c_str())) {
                CoreApplication::App().SendTask([parent = actor, this](auto&) {
                    if (parent)
                        parent->AddChild(m_layer.CreateActor()); 
                    else 
                        m_layer.CreateActor(); 
                });
            }
        };
        auto CreateMeshActor = [actor, this](const std::string & meshName) {
            if (ImGui::MenuItem(meshName.c_str())) {
                CoreApplication::App().SendTask([parent = actor, this, meshName](auto&) {
                    auto & actor = m_layer.CreateActor();
                    auto & comp = actor.AddComponent<ModelRenderComp>();
                    comp.Load(meshName);
                    if (parent) 
                        parent->AddChild(actor);
                });
            }
        };
        if (ImGui::BeginMenu("Create")) {
            CreateEmptyActor("Empty");
            if (ImGui::BeginMenu("Mesh")) {
                CreateMeshActor("Cone");
                CreateMeshActor("Cube");
                CreateMeshActor("Cylinder");
                CreateMeshActor("Gear");
                CreateMeshActor("Helix");
                CreateMeshActor("Pipe");
                CreateMeshActor("Plane");
                CreateMeshActor("Pyramid");
                CreateMeshActor("Sphere");
                CreateMeshActor("Torus");
                ImGui::EndMenu();
            }
            if (ImGui::BeginMenu("Light")) {
                if (ImGui::MenuItem("Direct Light")) {
                    CoreApplication::App().SendTask([parent = actor, this](auto&) {
                        auto & actor = m_layer.CreateActor();
                        auto & comp = actor.AddComponent<DirectLightComp>();
                        if (parent) 
                            parent->AddChild(actor);
                    });
                }
                if (ImGui::MenuItem("Point Light")) {
                    CoreApplication::App().SendTask([parent = actor, this](auto&) {
                        auto & actor = m_layer.CreateActor();
                        auto & comp = actor.AddComponent<PointLightComp>();
                        if (parent) 
                            parent->AddChild(actor);
                    });
                }
                if (ImGui::MenuItem("Ambient Light")) {
                    CoreApplication::App().SendTask([parent = actor, this](auto&) {
                        auto & actor = m_layer.CreateActor();
                        auto & comp = actor.AddComponent<AmbientLightSphereComp>();
                        if (parent) 
                            parent->AddChild(actor);
                    });
                }
                ImGui::EndMenu();
            }
            if (ImGui::MenuItem("Camera")) {
                CoreApplication::App().SendTask([parent = actor, this](auto&) {
                    auto & actor = m_layer.CreateActor();
                    auto & comp = actor.AddComponent<CameraComp>();
                    if (parent) 
                        parent->AddChild(actor);
                });
            }
            ImGui::EndMenu();
        }
    }

    void SceneWidget::OnFocusActor(Actor &actor)
    {
        auto & trans = actor.GetComponent<TransformComp>()->get();
        auto targetPos = glm::vec3{trans.GetModel() * glm::vec4{0.0f, 0.0f, 0.0f, 1.0f}};
        SignalFocusActor.Invoke(targetPos);
    }

    void SceneWidget::OnRightClickMenu()
    {
        auto project = m_layer.GetApp().GetDescriptor<Project>();
        if (!project) {
            return;
        }
        auto scene = project->get().GetScene();

        ImGui::PushID("RightClickMenu");
        if (ImGui::BeginPopupContextWindow()) {
            CreateMenu(nullptr);
            ImGui::EndPopup();
        }
        ImGui::PopID();
    }

} // namespace SEngine
